	;; d5 width-1
	;; d6 height-1
	;; d7 #$40000003 for plane A at 0x0
	;; d7 #$60000003 for plane B at 0x0
	;; destroys d7
ClearMap:	
	movem.l d4,-(a7)
ClearPlaneLoopOuter:	
	move.l  d7,(a4)
        move.w  d5,d4
ClearPlaneLoopInner:	
        move.w  #$0000,(a3)
        dbf    d4,ClearPlaneLoopInner
	addi.l  #$00800000,d7
	dbf    d6,ClearPlaneLoopOuter
	movem.l (a7)+,d4
	rts

	;; d7 #$40000003 for plane A at 0x0
	;; d7 #$60000003 for plane B at 0x0
	;; clear 64x28 in a plane that is 64x32
	;; destroys d5-d7
ClearPlane64By28In64By32:	
        moveq   #63,d5
        moveq   #27,d6
	bsr.s   ClearMap
	rts

	;; d7 #$40000003 for plane A at 0x0
	;; clear 64x28 in a plane that is 64x32
	;; destroys d6-d7
ClearHScroll32:	
        moveq   #31,d6
ClearHScroll32Loop:
	move.l	d7,(a4)
	move.w	#$0000,(a3)
	addi.l	#$00200000,d7
	dbf	d6,ClearHScroll32Loop
	rts

	;; same inputs to copy map
CopyMapAAndMakeBorderB:
	movem.l d4-d7,-(a7)
	bsr.s   CopyMap
	movem.l (a7)+,d4-d7
	;; only keep priority and palette in d4
	andi.w  #$E000,d4
	addi.l  #$1F7E0000,d7
	bsr.s   MakeBorder
	rts
	
	;; a5 has map to copy
	;; d4 has upper byte for tile
	;; d5 width-1
	;; d6 height-1
	;; d7 #$40000003 for plane A at 0x0
	;; d7 #$60000003 for plane B at 0x0
	;; clear 40x28 in a plane that is 64x32
	;; destroys inputs
CopyMap:	
	movem.w d3,-(a7)
	move.w	d5,d3
CopyMapLoopOuter:	
	move.l  d7,(a4)
        move.w  d3,d5
CopyMapLoopInner:	
	move.b	(a5)+,d4
        move.w  d4,(a3)
        dbf    d5,CopyMapLoopInner
	addi.l  #$00800000,d7
	dbf    d6,CopyMapLoopOuter
	movem.w (a7)+,d3
	rts

	;; same inputs as CopyMap
CopyMapWithSpaces:	
	movem.w d3,-(a7)
	move.w	d5,d3
CopyMapWithSpacesLoopOuter:	
	move.l  d7,(a4)
        move.w  d3,d5
CopyMapWithSpacesLoopInner:	
	move.b	(a5)+,d4
        move.w  d4,(a3)
        dbf    d5,CopyMapWithSpacesLoopInner
	addi.l  #$01000000,d7
	dbf    d6,CopyMapWithSpacesLoopOuter
	movem.w (a7)+,d3
	rts
	
	;; d4 has upper byte for tile
	;; d5 width-3
	;; d6 height-3
	;; d7 #$40000003 for plane A at 0x0
	;; d7 #$60000003 for plane B at 0x0
	;; clear 40x28 in a plane that is 64x32
	;; destroys inputs
MakeBorder:	
	;; d5=2 d6-2 would create the following map
	;; A4 A5 A5 A5 A6
	;; A7 A8 A8 A8 A9
	;; A7 A8 A8 A8 A9
	;; A7 A8 A8 A8 A9
	;; AA AB AB AB AC
	movem.w d3,-(a7)
	move.w  d5,d3
	;; north border
	move.l	d7,(a4)
	;; first tile of border, the rest of the routine uses offsets from this
	move.b  #$A4,d4
	move.w  d4,(a3)
	addq.b  #1,d4
MakeBorderNorthLoop:	
	move.w  d4,(a3)
        dbf    d5,MakeBorderNorthLoop
	addq.b  #1,d4
	move.w  d4,(a3)
	addi.l  #$00800000,d7
	addq.b  #3,d4
	;; middle border and fills
MakeBorderLoopOuter:	
	move.l  d7,(a4)
	subq.b  #2,d4
	move.w  d4,(a3)
	addq.b  #1,d4
        move.w  d3,d5
MakeBorderLoopInner:	
	move.w  d4,(a3)
        dbf    d5,MakeBorderLoopInner
	addq.b  #1,d4
	move.w  d4,(a3)
	addi.l  #$00800000,d7
	dbf    d6,MakeBorderLoopOuter
	;; south border
	move.l	d7,(a4)
	addq.b  #1,d4
	move.w  d4,(a3)
	addq.b  #1,d4
        move.w  d3,d5
MakeBorderSouthLoop:	
	move.w  d4,(a3)
        dbf    d5,MakeBorderSouthLoop
	addq.b  #1,d4
	move.w  d4,(a3)
	movem.w (a7)+,d3
	rts

	;; prefers green,blue,red
FadeOut:	
	moveq 	#$00,d0
	moveq 	#$00,d1
	moveq 	#$02,d2
FadeOutLoop:
	bsr.s	Fade
	bsr     WaitForVint
	move.b	d2,d0
	bsr.s	Fade
	bsr     WaitForVint
	move.b	d2,d1
	bsr.s	Fade
	bsr     WaitForVint
	addq.b	#2,d2
	cmpi.b	#$0E,d2
	ble.s	FadeOutLoop
FadeOutFake:	
	bset	#FadeOutFlag,Flags
	;; enable genesis display and disable vint
	move.w  #$8104,(a4)
	;; supervisor mode and ignore all interrupts
	move    #$2700,sr
	rts

	;; prefers green,blue,red
FadeIn:
	;; enable display and vint and genesis display
        move.w    #$8164,(a4)
	;; allow interrupts again
	and.w     #$F8FF,sr
	bclr	#FadeOutFlag,Flags
	beq.s	FadeInDone
	moveq 	#$0E,d0
	moveq 	#$0C,d1
	moveq 	#$0E,d2
FadeInLoop:
	bsr.s	Fade
	bsr     WaitForVint
	move.b	d1,d0
	bsr.s	Fade
	bsr     WaitForVint
	move.b	d1,d2
	bsr.s	Fade
	bsr     WaitForVint
	subq.b	#2,d1
	bge.s	FadeInLoop
FadeInDone:	
	rts

	;; d0,d1,d2 has fade values for b,g,r
	;; destroys a5,d3-d7
Fade:	
	;; vdp increment address by 1
	move.w  #$8f01,(a4)
	;; copy the palettes to VDP
        move.l    #$c0000000,(a4)
        move.w   #((EndPalettes-StartPalettes)/2-1),d7
        lea      StartPalettes(pc),a5
        clr.l    d3
FadePalettesCopy:
        move.w   (a5)+,d3
	clr.w	d6
	move.w	d3,d4
	moveq	#8,d5
	lsr.w	d5,d4
	andi.w	#$000E,d4
	cmp.b	d4,d0
	bge.s	ZeroBlue
	sub.b	d0,d4
	lsl.w	d5,d4
	or.w	d4,d6
ZeroBlue:
	move.w	d3,d4
	lsr.w	#4,d4
	andi.w	#$000E,d4
	cmp.b	d4,d1
	bge.s	ZeroGreen
	sub.b	d1,d4
	lsl.w	#4,d4
	or.w	d4,d6
ZeroGreen:
	andi.w	#$000E,d3
	cmp.b	d3,d2
	bge.s	ZeroRed
	sub.b	d2,d3
	or.w	d3,d6
ZeroRed:	
        move.l  d6,(a3)
        dbf    d7,FadePalettesCopy
	;; restore vdp increment address by 2
	move.w  #$8f02,(a4)
	rts

	;; a5 start of sprite table
SpriteLoad:	
	movem.l	 d0/a5-a6,-(a7)
	move.l   #StartSpriteRAM,a6
        move.w   #((EndSpriteRAM-StartSpriteRAM)/4-1),d0
SpriteLoadLoop:	
        move.l   (a5)+,(a6)+
        dbf      d0,SpriteLoadLoop
	movem.l	 (a7)+,d0/a5-a6
	rts

	;; a5 sprite1
	;; a6 sprite2
	;; sets z flag when collision, clears z flag when no collision
CheckSpriteCollision:
	movem.w	d0-d2,-(a7)
	;; collision check x
	;; get sprite 1 x
	move.w	6(a5),d0
	;; get sprite 2 x
	move.w	6(a6),d1
	;; determine width of sprite 2
	move.b	2(a6),d2
	andi.w	#$000C,d2
	addq.w	#4,d2
	lsl.w	#1,d2
	;; subtract width of sprite 2 from sprite 1 x
	sub.w	d2,d0
	;; compare (sprite 1 x - sprite 2 width) and (sprite 2 x)
	cmp.w	d0,d1
	ble.s	CheckSpriteCollisionNone
	;; restore sprite 1 x by adding back sprite 2 width that was subtracted
	add.w	d2,d0
	;; determine width of sprite 1
	move.b	2(a5),d2
	andi.w	#$000C,d2
	addq.w	#4,d2
	lsl.w	#1,d2
	;; add width of sprite 1 to sprite 1 x
	add.w	d2,d0
	;; compare (sprite 1 x + sprite 1 width) and (sprite 2 x)
	cmp.w	d0,d1
	bge.s	CheckSpriteCollisionNone
	;; collision check y
	move.w	(a5),d0
	move.w	(a6),d1
	move.b	2(a6),d2
	andi.w	#$0003,d2
	addq.w	#1,d2
	lsl.w	#3,d2
	sub.w	d2,d0
	cmp.w	d0,d1
	ble.s	CheckSpriteCollisionNone
	add.w	d2,d0
	move.b	2(a5),d2
	andi.w	#$0003,d2
	addq.w	#1,d2
	lsl.w	#3,d2
	add.w	d2,d0
	cmp.w	d0,d1
	bge.s	CheckSpriteCollisionNone
	ori     #$04,ccr
	bra.s	CheckSpriteCollisionDone
CheckSpriteCollisionNone:	
	andi    #$1B,ccr
CheckSpriteCollisionDone:	
	;; does not affect condition codes
	movem.w	(a7)+,d0-d2
	rts

	;; a5 sprite1 (bigger sprite)
	;; a6 sprite2
	;; sets z flag when inside, clears z flag when not fully inside
CheckSpriteInsideAnotherSprite:	
	movem.w	d0-d2,-(a7)
	;; inside check x
	;; get sprite 1 x
	move.w	6(a5),d0
	;; get sprite 2 x
	move.w	6(a6),d1
	;; compare (sprite 1 x) and (sprite 2 x)
	cmp.w	d0,d1
	blt.s	CheckSpriteInsideAnotherSpriteNone
	;; determine width of sprite 1
	move.b	2(a5),d2
	andi.w	#$000C,d2
	addq.w	#4,d2
	lsl.w	#1,d2
	;; add width of sprite 1 to sprite 1 x
	add.w	d2,d0
	;; determine width of sprite 2
	move.b	2(a6),d2
	andi.w	#$000C,d2
	addq.w	#4,d2
	lsl.w	#1,d2
	;; add width of sprite 2 to sprite 2 x
	add.w	d2,d1
	;; compare (sprite 1 x + sprite 1 width) and
	;; (sprite 2 x + sprite 2 width)
	cmp.w	d0,d1
	bgt.s	CheckSpriteInsideAnotherSpriteNone
	;; inside check y
	move.w	(a5),d0
	move.w	(a6),d1
	cmp.w	d0,d1
	blt.s	CheckSpriteInsideAnotherSpriteNone
	move.b	2(a5),d2
	andi.w	#$0003,d2
	addq.w	#1,d2
	lsl.w	#3,d2
	add.w	d2,d0
	move.b	2(a6),d2
	andi.w	#$0003,d2
	addq.w	#1,d2
	lsl.w	#3,d2
	add.w	d2,d1
	cmp.w	d0,d1
	bgt.s	CheckSpriteInsideAnotherSpriteNone
	ori     #$04,ccr
	bra.s	CheckSpriteInsideAnotherSpriteDone
CheckSpriteInsideAnotherSpriteNone:	
	andi    #$1B,ccr
CheckSpriteInsideAnotherSpriteDone:	
	;; does not affect condition codes
	movem.w	(a7)+,d0-d2
	rts


StartVDPSetup:
	;; based on sonic 1 vdp init
	; VDP $80 - 8-colour mode
	; VDP $81 - Megadrive mode, DMA disable
	dc.b $04,$04
	; VDP $82 - foreground nametable address
	; VDP $83 - window nametable address
	dc.b ($C000>>10),($F000>>10)
	; VDP $84 - background nametable address
	; VDP $85 - sprite table address
	dc.b ($E000>>13),($D800>>9)
	; VDP $86 - unused
	; VDP $87 - background colour
	dc.b 0,0
	; VDP $88 - unused
	; VDP $89 - unused
	dc.b 0,0
	; VDP $8A - HBlank register
	; VDP $8B - full v scroll, cell h scroll
	dc.b $FF,$02
	; VDP $8C - 40 cell display
	; VDP $8D - hscroll table address
	dc.b $81,($DC00>>10)
	; VDP $8E - unused
	; VDP $8F - VDP increment
	dc.b 0,1
	; VDP $90 - 64 cell hscroll size
	; VDP $91 - window h position
	dc.b 1,0
	; VDP $92 - window v position
	; unused byte
	dc.b 0,0
EndVDPSetupEven:
EndVDPSetup:	equ	EndVDPSetupEven-1
